package com.iclub.filter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.iclub.model.ServiceException;
import com.iclub.util.GlobalParamUtil;



/**
 * 
 * @description: 请求参数是否有效的拦截器
 */
public class IClubCheckRequestData implements HandlerInterceptor{
	
	private static Logger log_detail = Logger.getLogger("DETAIL");
	
	/** 
     * preHandle方法是进行处理器拦截用的，顾名思义，该方法将在Controller处理之前进行调用，SpringMVC中的Interceptor拦截器是链式的，可以同时存在 
     * 多个Interceptor，然后SpringMVC会根据声明的前后顺序一个接一个的执行，而且所有的Interceptor中的preHandle方法都会在 
     * Controller方法调用之前调用。SpringMVC的这种Interceptor链式结构也是可以进行中断的，这种中断方式是令preHandle的返 
     * 回值为false，当preHandle的返回值为false的时候整个请求就结束了。 
     */
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		// 请求应用路径
		String uri = request.getRequestURI();
		// 请求接口名称
		String funCode = trim(uri.substring(uri.lastIndexOf("/")+1));
		if(!"".equals(funCode)){
			if(!checkParameters(funCode,request)){
				throw new ServiceException("0001","请求参数有误，请检查相关参数合法性");
			}
			return true;
		}
		return false;
	}
	/**
	 * 循环参数校验
	 * desc:
	 * @param funcode
	 * @param request
	 * @return
	 */
	public boolean checkParameters(String funcode, HttpServletRequest request) {
		String keys = GlobalParamUtil.getGlobalParam(funcode);
		if ("".equals(keys)) {
			log_detail.info(String.format("Service %s found no check info",
					funcode));
			return true;
		}
		log_detail.info(String.format("funCode[%s] request parameters checked by regex beginning", funcode));
		// 校验规则以$分隔
		String[] chkKeys = keys.split("\\$");
		for (String key : chkKeys) {
			String[] regConfigs = key.split("\\|");
			// 参数校验配置错误
			if (null == regConfigs || regConfigs.length != 2) {
				log_detail.error("service interface regex config :" + regConfigs);
				return false;
			}
			// first key
			String param_key = regConfigs[0];
			// second regex
			String regex = regConfigs[1];
			// check key is empty
			if ("".equals(param_key)) {
				log_detail.warn(String.format("request parameter no config key", key));
    			continue;
    		}
			// check regex is empty
			if ("".equals(regex)) {
				log_detail.warn(String.format("request parameter %s found no regex info", key));
				continue;
			}
			 // check is not must do
            boolean must = isOptional(param_key);
            // not must do remove []
            if(must){
            	param_key = removBrackets(param_key);
            }
            //  geting request parameter
            String param_value = trim(request.getParameter(param_key));
            // second request attributte
            if("".equals(param_value)){
            	param_value = trim(request.getAttribute(param_key));
            }
            // checking value
            boolean checkResult = check(param_key,param_value,must,regex);
            // check result
    		if(checkResult) {
    			continue;
    		} else {
    			return false;
    		}
		}
		log_detail.info(String.format("funCode[%s] request parameters checked by regex success", funcode));
		return true;
	}
	/**
	 * 参数合法性验证
	 * desc:
	 * @param key
	 * @param value
	 * @param must
	 * @param regex
	 * @return
	 */
	protected boolean check(String key, String value, boolean must,String regex) {
		if ("".equals(value)) {
			// 必须字段为空，抛出异常
			if (!must) {
				log_detail.warn(String.format("must request parameter [%s] value is empty", key));
				return false;
			}
		} else {
            // 去掉空格字符
            value = value.replaceAll("\\s*","");
			// 不符合正则表达式
			if (!value.matches(regex)) {
				log_detail.warn(String.format("request parameter [%s] value[%s] is not match regex[%s]", key,value,regex));
				return false;
			}
		}
		return true;
	}
	// remove start[ and end ]
	private String removBrackets(String key) {
		return key.substring(1, key.length() - 1);
	}
	// check is must or not
	private boolean isOptional(String key) {
		return key.startsWith("[") && key.endsWith("]");
	}
	// remove space char
	protected String trim(Object src) {
		return src == null ? "" : src.toString().trim();
	}
	/** 
     * 这个方法只会在当前这个Interceptor的preHandle方法返回值为true的时候才会执行。postHandle是进行处理器拦截用的，它的执行时间是在处理器进行处理之 
     * 后，也就是在Controller的方法调用之后执行，但是它会在DispatcherServlet进行视图的渲染之前执行，也就是说在这个方法中你可以对ModelAndView进行操 
     * 作。这个方法的链式结构跟正常访问的方向是相反的，也就是说先声明的Interceptor拦截器该方法反而会后调用，这跟Struts2里面的拦截器的执行过程有点像， 
     * 只是Struts2里面的intercept方法中要手动的调用ActionInvocation的invoke方法，Struts2中调用ActionInvocation的invoke方法就是调用下一个Interceptor 
     * 或者是调用action，然后要在Interceptor之前调用的内容都写在调用invoke之前，要在Interceptor之后调用的内容都写在调用invoke方法之后。 
     */
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		
	}
	/** 
     * 该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。该方法将在整个请求完成之后，也就是DispatcherServlet渲染了视图执行， 
     * 这个方法的主要作用是用于清理资源的，当然这个方法也只能在当前这个Interceptor的preHandle方法的返回值为true时才会执行。 
     */
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
	}
	
}
